
/* Copyright (C) 2025 sangong All Rights Reserved.
 
 File Name: client.c
 Author   : sangong
 Creation Date: 2025-06-05
 INFO     :
*/
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<sqlite3.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<dirent.h>
#include<sys/stat.h>
#include<fcntl.h>
#define size 64    //留给用户的帐号密码字符的缓冲区大小
void login_register_handler(int sockfd,sqlite3 *db);
sqlite3 * open_sqlite3();
int judge_account( void *para,int f_num,char **f_value,char **f_name);
int ret_judge_account = 0;
void signal_handler(int no);
void get_msg(int nfd,struct sockaddr_in caddr);
void get_file(int sockfd);
void opt_file(int sockfd,struct sockaddr_in caddr);
int main(int argc,char *argv[])
{
     
     sqlite3 *db = open_sqlite3();
    
     int sockfd = socket(AF_INET,SOCK_STREAM,0);
     if( sockfd < 0){
       perror("socket");
       exit(-1);
     }
     int reuseaddr = 1;
     int k = setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuseaddr,sizeof reuseaddr);
     if( k != 0){
       perror("setsockopt");
       exit(-1);
     }
    struct sockaddr_in saddr,caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(8000);
    saddr.sin_addr.s_addr = inet_addr("192.168.194.1");
    int s_len = sizeof saddr;
    int c_len = sizeof caddr;
    int ret = bind(sockfd,(struct sockaddr*)&saddr,s_len);
    if(ret < 0){
      perror("bind");
      exit(-1);
    }
    ret = listen(sockfd,200);
    if(ret < 0){
      perror("listen");
      exit(-1);
    }

    pid_t pid;

    //处理孤儿进程
    
    signal(SIGCHLD,signal_handler);


    //开多个进程处理客户端
    while(1){
     int connfd = accept(sockfd,(struct sockaddr*)&caddr,&c_len);

     if(ret < 0){
       perror("accept");
       exit(-1);
     }
     else
	     printf("%s link success\n",inet_ntoa(caddr.sin_addr));
     pid = fork();
     if(pid < 0){
       perror("fork");
       exit(-1);
     }
     else if( pid == 0){
   //用户登陆注册
         int nfd = connfd;
         login_register_handler(nfd,db);
     //登陆注册结束后，收发消息
      //   get_msg(nfd,caddr);
     //获取服务器上的文件列表
         get_file(nfd); 
     //文件操作
         opt_file(nfd,caddr);
	 break;
     } 
    else {
       continue;
     }
   }
}

//文件操作
void opt_file(int sockfd,struct sockaddr_in caddr){
 
    char opt_1[2] = {0};
    int ret = 0;
B:  ret =  read(sockfd,opt_1,2);
    if(ret <= 0){
      printf("%s is quit\n",inet_ntoa(caddr.sin_addr));
      //perror("read");
      exit(-1);
    }
  //如果客户选择下载
  if( opt_1[0] == '1'){
    char download_fn[256] = {0};
    ret = read(sockfd,download_fn,256);
    printf("%s want download:%s\n",inet_ntoa(caddr.sin_addr),download_fn);
    //判断文件名是否正确
    struct dirent *file;
    DIR *dp;
    dp = opendir(".");
    int find_flag = 0;
    while(file = readdir(dp)){
     if(file->d_name[0] == '.')
	     continue;
     if(strncmp(file->d_name,"a.out",strlen("a.out")) ==0 )
	     continue;
     if(strncmp(file->d_name,"server.c",strlen("server.c")) == 0)
	     continue;
     if(strncmp(file->d_name,"my.db",strlen("my.db")) == 0)
	     continue;
      if( strncmp(file->d_name,download_fn,strlen(download_fn)) == 0)
	      find_flag = 1;
    }
    //如果文件名正确，则发送文件
    if( find_flag){
      ret = write(sockfd,"1",1);
      if( ret <= 0){
        perror("write");
	exit(-1);
      }
     //发送数据
     char buf[1024] = {0};
     char buf_len[5] = {0};
     int fd = open(download_fn,O_RDONLY);
     while(1){
       memset(buf,0,1024);
       memset(buf_len,0,5);
       ret = read(fd,buf,sizeof(buf));
       if( ret <= 0){
         close(fd);
	 break;
       }
       sprintf(buf_len,"%d",ret);  //将读到的字节个数转换为字符
       if( 0>= write(sockfd,buf_len,5)){ //发送即将传入的字节个数（字符版）
         perror("write");
	 break;
       }
       if( 0>= write(sockfd,buf,ret)){  //发送buf
         perror("write");
	 break;
       }
     }
     printf("%ssend success\n",download_fn);
     goto B;
    }
    else
    {
      ret = write(sockfd,"2",1);
      goto B;
    }
  }
  //如果用户选择上传
  else if (opt_1[0] == '2'){
     char access[1] = {0};
     read(sockfd,access,1);
     //客户端正确操作
     if(access[0] =='3'){
       char up_fn[64] = {0};
       printf("%s want upload:%s\n",inet_ntoa(caddr.sin_addr),up_fn);
       read(sockfd,up_fn,64);
       char buf[1024] ={0};
       char size_buf[5] = {0};
       int len = 0;
       int fd = open(up_fn,O_CREAT|O_TRUNC|O_RDWR,0666);
       if( fd <0 ){
         perror("open");
	 exit(-1);
       }
	 while(1){

          while( 5 >recv(sockfd,size_buf,5,MSG_PEEK));
	  recv(sockfd,size_buf,5,0);
	  len = atoi(size_buf);
	  while(len > recv(sockfd,buf,len,MSG_PEEK));
	  recv(sockfd,buf,len,0);
	  write(fd,buf,len);
	  if( len < 1024)
		  break;
      }
	 printf("%s recv success\n",up_fn);
	 goto B;
     }
     //客户端输入文件名或路径错误
     else{
       goto B;
     }
  
  }
  else{
    goto B;
  }


}
//发送服务器端的文件
void get_file(int sockfd){
  struct dirent *file;
  DIR *dp;
  dp = opendir(".");
  int ret;
  while(file = readdir(dp)){
     if(file->d_name[0] == '.')
	     continue;
     if(strncmp(file->d_name,"a.out",strlen("a.out")) ==0 )
	     continue;
     if(strncmp(file->d_name,"server.c",strlen("server.c")) == 0)
	     continue;
     if(strncmp(file->d_name,"my.db",strlen("my.db")) == 0)
	     continue;
     char judge_file[1] = {0};
     judge_file[0] = '1';
     ret = write(sockfd,judge_file,1);
     if(ret <= 0){
       perror("write");
       exit(-1);
     }
     file->d_name[strlen(file->d_name)] = '\0';
     ret = write(sockfd,file->d_name,256);
     if( ret <= 0){
       perror("write");
       exit(-1);
     }
  }
     char judge_file[1] = {0};
     judge_file[0] = '0';
     ret = write(sockfd,judge_file,1);
     if(ret <= 0){
       perror("write");
       exit(-1);
     }
}
//收发消息
void get_msg(int nfd,struct sockaddr_in caddr){
     char buf[64] = {0};
     while(1){
       memset(buf,0,64);
       int ret = recv(nfd,buf,64,0);
       if( ret <= 0){
	 printf("%s is quit\n",inet_ntoa(caddr.sin_addr));
         break;
       }
       printf("recv:%s",buf);
     }

}
//打开数据库
sqlite3 * open_sqlite3(){
     
     sqlite3 *db;
     int ret = sqlite3_open("my.db",&db);
     if (ret != 0){
      printf("打开数据库失败");
      printf("error:%s\n",sqlite3_errmsg(db));
      exit(-1);
     }
     return db;
}
//处理用户的登陆注册操作
void login_register_handler(int sockfd,sqlite3 * db){
  
	char *errmsg = NULL;
	int ret = 0;
	char opt[2] = {0};
A:	ret = read(sockfd,opt,2);
	if(ret <= 0){
	 perror("88read");
	 exit(-1);
	 goto A;
	}
       
	//处理登陆操作 
        if( opt[0] == '1'){
	   char account[64] = {0};
	   char pass[64] = {0};
	   while( size > recv(sockfd,account,64,MSG_PEEK));
	   ret = recv(sockfd,account,64,0);
	   if( ret < 0){
	     perror("2_account_recv");
	     exit(-1);
	   }
	   while( size > recv(sockfd,pass,64,MSG_PEEK));
	   ret = recv(sockfd,pass,64,0);
	   if( ret < 0){
	     perror("2_pass_recv");
	     exit(-1);
	   }
	   //判断账号密码
	   
	   //1.帐号密码都正确
	   char sql_judge[1024] = {0};
	   sprintf(sql_judge,"select * from usr where account='%s' and pass='%s';",account,pass);
	   ret = sqlite3_exec(db,sql_judge,judge_account,NULL,&errmsg);
	   if( ret != 0){
	     printf("error:%s\n",errmsg);
	     exit(-1);
	   }
	   if( ret_judge_account ){
	      ret_judge_account = 0;
	      char agree_to_login[1] = {0};
	      agree_to_login[0] = '1';
	      ret = write(sockfd,agree_to_login,1);
	      if( ret < 0){
	        perror("write");
	        exit(-1);
	      }
	   }
	   //2.账号密码不正确，重新返回登陆注册
	   else{
	      ret_judge_account = 0;
	      char agree_to_login[1] = {0};
	      agree_to_login[0] = '2';
	      ret = write(sockfd,agree_to_login,1);
	      if( ret < 0){
	        perror("write");
	        exit(-1);
	      }
	      goto A;
	   }
	
	}
	//处理注册操作，判断并将用户的数据加入数据库（用户表）
	if( opt[0] == '2'){
	   char account[64] = {0};
	   char pass[64] = {0};
	   while( size > recv(sockfd,account,64,MSG_PEEK));
	   ret = recv(sockfd,account,64,0);
	   if( ret < 0){
	     perror("2_account_recv");
	     exit(-1);
	   }
	   while( size > recv(sockfd,pass,64,MSG_PEEK));
	   ret = recv(sockfd,pass,64,0);
	   if( ret < 0){
	     perror("2_pass_recv");
	     exit(-1);
	   }
	   //判断用户表中是否存在该账号
           char sql_select[1024] = {0}; 
           sprintf(sql_select,"select *from usr where account='%s';",account);
	   ret = sqlite3_exec(db,sql_select,judge_account,NULL,&errmsg);  //如果没有记录时，不会触发回调函数
           if( ret != 0){
	     printf("176error:%s\n",errmsg);
	     exit(-1);
	   }
	   //将用户的账号和密码加入数据库（用户表）
	   if( !ret_judge_account){
	   ret_judge_account = 0;
	   char agree_to_register[1] = {0};
	   agree_to_register[0] = '1';
	   ret = write(sockfd,agree_to_register,1);
	   if( ret < 0){
	     perror("write");
	     exit(-1);
	   }
	   char name[64] = {0};
	   ret = read(sockfd,name,64);
	   if( ret <0){
	      perror("read");
	      exit(-1);
	   }
	   char sql_add[1024] = {0};
	   sprintf(sql_add,"insert into usr values ('%s','%s','%s');",account,pass,name);
	   ret = sqlite3_exec(db,sql_add,NULL,NULL,&errmsg);
	   if( ret != 0){
            printf("sql_add:error:%s\n",errmsg);
	    exit(-1);
	   }
	   goto A;
	  }
	   else
	   {
             ret_judge_account = 0;
	     char disagree_to_register[1] = {0};
	     disagree_to_register[0] = '2';
	     ret = write(sockfd,disagree_to_register,1);
	     if( ret < 0){
	       perror("write");
	       exit(-1);
	     }
	     goto A;
	   }
	}
}

void signal_handler(int no){
   if( no == SIGCHLD)
	   wait(NULL);

}
int judge_account( void *para,int f_num,char **f_value,char **f_name){
    ret_judge_account = 1;
    return 0;
}
